{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e865ba60-b8b6-4dbb-b1d9-fd6883d15d86",
   "metadata": {},
   "source": [
    "# The Swap Test Algorithm\n",
    "\n",
    "The swap test is a quantum function that checks the overlap between two quantum states: the inputs of the function are two quantum registers of the same size, $|\\psi_1\\rangle, \\,|\\psi_2\\rangle$, and it returns as an output a single \"test\" qubit whose state encodes the overlap between the two inputs: $|q\\rangle_{\\rm test} = \\alpha|0\\rangle + \\sqrt{1-\\alpha^2}|1\\rangle$, with\n",
    "$$\n",
    "\\alpha^2 = \\frac{1}{2}\\left(1+|\\langle \\psi_1 |\\psi_2 \\rangle |^2\\right).\n",
    "$$\n",
    "Thus, the probability of measuring the test qubit at state $|0\\rangle$ is $1$ if the states are identical (up to a global phase) and 0.5 if the states are orthogonal to each other.\n",
    "\n",
    "The quantum model starts with an H gate on the test qubit, followed by swapping between the two states controlled on the test qubit (a controlled-SWAP gate for each of the qubits in the two states), and a final H gate on the test qubit:\n",
    "\n",
    "<center>\n",
    "<table><tr>\n",
    "<td> <img src=\"https://docs.classiq.io/resources/swap_test_closed.png\" style=\"width:100%\">\n",
    "<td> <img src=\"https://docs.classiq.io/resources/swap_test_opened.png\" style=\"width:100%\">\n",
    "</tr></table>\n",
    "<figcaption align = \"middle\"> Closed (left panel) and opened (right panel) visualization of the swap test algorithm </figcaption>\n",
    "</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "001ceb9f-5864-45fc-a1aa-6801e309e4c0",
   "metadata": {},
   "source": [
    "Prepare two random states:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "3172465a-c12d-4636-8092-430c8506622f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:30:58.002513Z",
     "iopub.status.busy": "2024-05-07T14:30:58.002260Z",
     "iopub.status.idle": "2024-05-07T14:30:58.132861Z",
     "shell.execute_reply": "2024-05-07T14:30:58.132021Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "np.random.seed(12)\n",
    "\n",
    "NUM_QUBITS = 3\n",
    "amps1 = 1 - 2 * np.random.rand(\n",
    "    2**NUM_QUBITS\n",
    ")  # vector of 2^3 numbers in the range [-1,1]\n",
    "amps2 = 1 - 2 * np.random.rand(2**NUM_QUBITS)\n",
    "amps1 = amps1 / np.linalg.norm(amps1)  # normalize the vector\n",
    "amps2 = amps2 / np.linalg.norm(amps2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec3644ea-bbf1-41a5-b412-7b68e83a5cca",
   "metadata": {},
   "source": [
    "Create a model and synthesize:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "eb3efd6a-ed62-4607-a297-f903f2415c93",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:30:58.138930Z",
     "iopub.status.busy": "2024-05-07T14:30:58.137654Z",
     "iopub.status.idle": "2024-05-07T14:31:03.338893Z",
     "shell.execute_reply": "2024-05-07T14:31:03.337995Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import (\n",
    "    Output,\n",
    "    QArray,\n",
    "    QBit,\n",
    "    create_model,\n",
    "    execute,\n",
    "    prepare_amplitudes,\n",
    "    qfunc,\n",
    "    set_execution_preferences,\n",
    "    swap_test,\n",
    "    synthesize,\n",
    ")\n",
    "from classiq.execution import ExecutionPreferences\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(test: Output[QBit]):\n",
    "\n",
    "    state1 = QArray(\"state1\")\n",
    "    state2 = QArray(\"state2\")\n",
    "    prepare_amplitudes(amps1.tolist(), 0.0, state1)\n",
    "    prepare_amplitudes(amps2.tolist(), 0.0, state2)\n",
    "    swap_test(state1, state2, test)\n",
    "\n",
    "\n",
    "qmod = create_model(main)\n",
    "qmod = set_execution_preferences(\n",
    "    qmod, execution_preferences=ExecutionPreferences(num_shots=100_000)\n",
    ")\n",
    "qprog = synthesize(qmod)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eeaa11bb-4a3a-4172-bc29-1bb8860f232a",
   "metadata": {},
   "source": [
    "## swap test - qmod implementations\n",
    "The swap test is defined as a library function in the qmod language (definition can be found on our [public github](https://github.com/Classiq/classiq-library/blob/main/functions/open_library_definitions/swap_test.qmod ) ).\n",
    "Users can easily add their own functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "071fefd6-0e34-441c-b4f0-7ebb86586d6e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:31:03.344383Z",
     "iopub.status.busy": "2024-05-07T14:31:03.342895Z",
     "iopub.status.idle": "2024-05-07T14:31:03.507873Z",
     "shell.execute_reply": "2024-05-07T14:31:03.507220Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/56682c53-15bf-4d1f-81e2-1121ce978b16?version=0.41.0.dev39%2B79c8fd0855\n"
     ]
    }
   ],
   "source": [
    "from classiq import show\n",
    "\n",
    "show(qprog)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "53828e56-cb64-4c7d-8624-92ecfd3b7e81",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:31:03.516412Z",
     "iopub.status.busy": "2024-05-07T14:31:03.511049Z",
     "iopub.status.idle": "2024-05-07T14:31:03.529974Z",
     "shell.execute_reply": "2024-05-07T14:31:03.529336Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import write_qmod\n",
    "\n",
    "write_qmod(qmod, \"swap_test\", decimal_precision=15)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7df3372c-02dd-4026-ac7b-042492e3cb31",
   "metadata": {},
   "source": [
    "Verify the results:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "deea6297-699c-44c2-9d39-52cf44010a15",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:31:03.534246Z",
     "iopub.status.busy": "2024-05-07T14:31:03.533227Z",
     "iopub.status.idle": "2024-05-07T14:31:04.983428Z",
     "shell.execute_reply": "2024-05-07T14:31:04.982620Z"
    }
   },
   "outputs": [],
   "source": [
    "res = execute(qprog).result()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "959995ad-aa6b-4b31-a25b-95396f8ffa7f",
   "metadata": {},
   "source": [
    "## Comparing the measured overlap with the exact overlap\n",
    "Using the expected probability of measuring the state $|0\\rangle$ as defined above:\n",
    "$$\n",
    "\\alpha^2 = \\frac{1}{2}\\left(1+|\\langle \\psi_1 |\\psi_2 \\rangle |^2\\right).\n",
    "$$\n",
    "we extract the overlap $|\\langle \\psi_1 |\\psi_2 \\rangle |^2=\\sqrt{2 P\\left(q_{\\text{test}}=|0\\rangle\\right)-1}$  \n",
    "The exact overlap is computed with the dot product of the two state-vectors.\n",
    "Note that for the sake of this demonstration we execute this circuit $100,000$ times to improve the precision of the probability estimate. This is usually not required in actual programs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "c95fc7a8-c609-4734-95cb-55054d593dbb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:31:04.988036Z",
     "iopub.status.busy": "2024-05-07T14:31:04.986986Z",
     "iopub.status.idle": "2024-05-07T14:31:04.992140Z",
     "shell.execute_reply": "2024-05-07T14:31:04.991431Z"
    }
   },
   "outputs": [],
   "source": [
    "overlap_from_swap_test = np.sqrt(\n",
    "    2 * res[0].value.counts[\"0\"] / sum(res[0].value.counts.values()) - 1\n",
    ")\n",
    "exact_overlap = np.abs(amps1 @ amps2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "b226e470-bb5c-4cce-93e1-3372c58df3f9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:31:04.996623Z",
     "iopub.status.busy": "2024-05-07T14:31:04.995461Z",
     "iopub.status.idle": "2024-05-07T14:31:05.002142Z",
     "shell.execute_reply": "2024-05-07T14:31:05.001461Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "States overlap from Swap-Test result: 0.4673970474874655\n",
      "States overlap from classical calculation: 0.46972037234759095\n"
     ]
    }
   ],
   "source": [
    "print(\"States overlap from Swap-Test result:\", overlap_from_swap_test)\n",
    "print(\"States overlap from classical calculation:\", exact_overlap)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "27a9308b-5840-44c5-b7b7-7299d8d6175a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:31:05.006570Z",
     "iopub.status.busy": "2024-05-07T14:31:05.005418Z",
     "iopub.status.idle": "2024-05-07T14:31:05.011253Z",
     "shell.execute_reply": "2024-05-07T14:31:05.010541Z"
    }
   },
   "outputs": [],
   "source": [
    "RTOL = 0.05\n",
    "assert np.isclose(\n",
    "    overlap_from_swap_test, exact_overlap, RTOL\n",
    "), f\"\"\"\n",
    "The quantum result is too far than classical one, by a relative tolerance of {RTOL}. Please verify your parameters\"\"\""
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
